package cn.dkdoo.utils.config.transactionalManager;

import cn.dkdoo.utils.common.ReflectPool;
import cn.dkdoo.utils.common.ThreadLocalPool;
import cn.dkdoo.utils.config.exception.DkdTransactionException;
import cn.dkdoo.utils.config.rollbackData.*;
import cn.dkdoo.utils.config.transactionInfo.TransactionInfoAbstract;
import cn.dkdoo.utils.utils.DkdSpringUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author nicky
 * @date 2021/5/10 下午3:22
 */
public abstract class TransactionalManagerAbstract {
    private Logger logger = LoggerFactory.getLogger(TransactionalManagerAbstract.class);

    public abstract SaveRollbackDataAbstract getSaveRollbackData();

    public final void commit() {
        TransactionInfoAbstract info = ThreadLocalPool.TRAINFO_MANAGER.getCur();
        if (info == null) {
            logger.warn("cmoit-事物号：【{}】,没有开启事物");
            return;
        }
        if (!info.checkIsNewTransaction() || (CollectionUtils.isEmpty(info.getSelfs()) && MapUtils.isEmpty(info.getDatas()))) {
            logger.warn("cmoit-事物号：【{}】,没有回滚数据", info.getTraNo());
            return;
        }
        this.doCommit(info);
        getSaveRollbackData().doSave(info.getTraNo(), info);
    }

    protected void doCommit(TransactionInfoAbstract rollbackDataSelves) {
        //预留给子类重写
    }

    public final void rollback(String traNo) {
        TransactionInfoAbstract info = this.getSaveRollbackData().doGet(traNo);
        if (info == null) {
            logger.warn("rollback-事物号：【{}】,没有回滚数据", traNo);
            return;
        }

        Map<String, RollbackDataTable> datas = info.getDatas();
        doDatas(traNo, datas);

        List<RollbackDataSelf> selfDatas = info.getSelfs();
        doSelfDatas(traNo, selfDatas);

        this.doRollback(info);
        logger.info("事物号：【{}】,回滚成功", traNo);
    }

    private void doDatas(String traNo, Map<String, RollbackDataTable> datas) {
        if (MapUtils.isEmpty(datas)) {
            return;
        }
        for (Map.Entry<String, RollbackDataTable> dataEntry : datas.entrySet()) {
            try {
                String collectionName = dataEntry.getKey();
                RollbackDataTable collectionData = dataEntry.getValue();
                IDoRollback iDoRollback = DkdSpringUtils.getBeanByBeanName(collectionData.getBeanName());
                iDoRollback.doRollback(collectionName, collectionData.getDataClass(), collectionData);
                if (!StringUtils.isEmpty(collectionData.getEsBean())) {
                    IDoRollback iDoRollbackEs = DkdSpringUtils.getBeanByBeanName(collectionData.getEsBean());
                    iDoRollbackEs.doRollback(collectionName, collectionData.getDataClass(), collectionData);
                }
            } catch (Exception ex) {
                logger.error("事物号：【{}】, 事务信息{}, 回滚失败", traNo, dataEntry.getKey(), ex);
            }
        }
    }

    private void doSelfDatas(String traNo, List<RollbackDataSelf> selfDatas) {
        if (CollectionUtils.isEmpty(selfDatas)) {
            return;
        }
        for (int i = (selfDatas.size() - 1); i >= 0; i--) {
            try {
                boolean hasRollback = false;
                RollbackDataSelf rollbackDataSelf = selfDatas.get(i);
                String beanName = rollbackDataSelf.getBeanName();
                String methodName = rollbackDataSelf.getMethodName();
                Collection<?> args = rollbackDataSelf.getArgs();
                Object obj = DkdSpringUtils.getBeanByBeanName(beanName);
                //todo-nicky 提升反射效率 存入map
                Method method = ReflectPool.getMethod(rollbackDataSelf.getMethodId());
                if (method != null && method.getName().equals(rollbackDataSelf.getMethodName())) {
                    try {
                        method.invoke(obj, args.toArray());
                        hasRollback = true;
                        continue;
                    } catch (Exception ex) {
                        logger.error("事物号：【{}】,回滚-缓存 调用失败['beanName':{},'curMethodName':{}]", traNo, beanName, method.getName(), ex);
                    }
                }
                for (Method declaredMethod : obj.getClass().getMethods()) {
                    try {
                        if (declaredMethod.getName().equals(methodName)) {
                            declaredMethod.setAccessible(true);
                            declaredMethod.invoke(obj, args.toArray());
                            hasRollback = true;
                            break;

                        }
                    } catch (Exception ex) {
                        logger.error("事物号：【{}】,回滚-for 调用失败['beanName':{},'curMethodName':{}]", traNo, beanName, declaredMethod.getName(), ex);
                    }
                }
                if (!hasRollback) {
                    throw new DkdTransactionException("事物号：【{}】,回滚失败，未找到对应的回滚方法{}.{}", traNo, beanName, methodName);
                }
            } catch (Exception ex) {
                logger.error("事物号：【{}】,回滚失败", traNo, ex);
            }
        }
    }

    protected void doRollback(TransactionInfoAbstract rollbackDataSelves) {
        //预留给子类重写
    }
}
